const Helpers = require("./helpers");
const Asserts = require("../utils/asserts");
const assert = require("assert");

Feature("Empty labels");

const { examples, Utils } = require("../examples/");

function isLabelType(type) {
  return type.toLowerCase().endsWith("labels");
}
function isLabels(val, key) {
  return isLabelType(key);
}

examples.forEach((example) => {
  const { annotations, config, data, result = annotations[0].result, title } = example;

  Scenario(`Different from_name -> ${title}`, async ({ I, LabelStudio, AtOutliner, AtAudioView }) => {
    let { result = annotations[0].result } = example;

    LabelStudio.setFeatureFlags({
      ff_front_dev_2715_audio_3_280722_short: true,
    });

    result = result.filter((res) => isLabelType(res.type));
    const params = { annotations: [{ id: "test", result }], data };
    const configTree = Utils.parseXml(config);

    Utils.xmlForEachNode(configTree, (node) => {
      if (node["#name"].toLowerCase().endsWith("labels") && node.$) {
        node.$.name = `Not-${node.$.name}`;
      }
    });
    params.config = Utils.renderXml(configTree);
    const regionsCount = Utils.countRegionsInResult(result);

    I.amOnPage("/");
    LabelStudio.init(params);
    LabelStudio.waitForObjectsReady();
    AtOutliner.seeRegions(regionsCount);

    if (Utils.xmlFindBy(configTree, (node) => node["#name"] === "Audio")) {
      await AtAudioView.waitForAudio();
    }
    if (regionsCount) {
      const restored = await LabelStudio.serialize();

      Asserts.notDeepEqualWithTolerance(result, restored, 1);
      for (let i = result.length; i--; ) {
        Asserts.deepEqualWithTolerance(
          Helpers.omitBy(result[i], (val, key) => key === "from_name" || isLabels(val, key)),
          Helpers.omitBy(restored[i], (val, key) => key === "from_name" || isLabels(val, key)),
          1,
        );
      }
    }
  });

  // For classifications that scenario does not make sense
  if (title.includes("Classifications,")) return;
  Scenario(`Nonexistent from_name -> ${title}`, async ({ I, LabelStudio, AtTopbar, AtOutliner }) => {
    const params = { annotations: [{ id: "test", result }], data };
    const configTree = Utils.parseXml(config);

    Utils.xmlFilterNodes(configTree, (node) => {
      return !node["#name"].toLowerCase().endsWith("labels");
    });
    params.config = Utils.renderXml(configTree);
    const regionsCount = Utils.countRegionsInResult(result);

    I.amOnPage("/");
    LabelStudio.init(params);
    AtTopbar.see("Update");
    AtOutliner.dontSeeRegions(regionsCount);
    AtOutliner.dontSeeRegions();
  });
});

const SINGLE_TYPE = "single";
const MULTIPLE_TYPE = "multiple";

[SINGLE_TYPE, MULTIPLE_TYPE].forEach((type) => {
  Scenario(`Making labels empty -> choice="${type}"`, async ({ I, LabelStudio, AtOutliner, AtAudioView, AtLabels }) => {
    async function expectSelectedLabels(expectedNum) {
      const selectedLabelsNum = await I.grabNumberOfVisibleElements(AtLabels.locateSelected());

      assert.strictEqual(selectedLabelsNum, expectedNum);
    }
    async function clickLabelWithLengthExpection(labelNumber, expectedLength, expectSelectedNum) {
      AtLabels.clickLabel(`${labelNumber}`);
      const restored = await LabelStudio.serialize();

      assert.strictEqual(restored[0].value.labels.length, expectedLength);
      await expectSelectedLabels(expectSelectedNum);
      I.pressKey(["u"]);
      await expectSelectedLabels(0);
      AtOutliner.clickRegion(1);
      await expectSelectedLabels(expectSelectedNum);
    }
    async function clickLabelWithSelectedExpection(labelNumber, expectSelectedNum) {
      AtLabels.clickLabel(`${labelNumber}`);
      await expectSelectedLabels(expectSelectedNum);
    }

    const audioRegions = require("../examples/audio-regions");
    const { annotations, config, data } = audioRegions;
    let { result = annotations[0].result } = require("../examples/audio-regions");

    result = result.filter((r) => isLabelType(r.type)).filter((r, idx) => !idx);
    const params = { annotations: [{ id: "test", result }], data, config };
    const configTree = Utils.parseXml(config);

    Utils.xmlForEachNode(configTree, (node) => {
      if (node["#name"].toLowerCase().endsWith("labels") && node.$) {
        node.$.allowempty = true;
        node.$.choice = type;
      }
    });
    params.config = Utils.renderXml(configTree);

    const regionsCount = Utils.countRegionsInResult(result);

    I.amOnPage("/");
    LabelStudio.init(params);
    await AtAudioView.waitForAudio();
    AtOutliner.seeRegions(regionsCount);

    AtOutliner.clickRegion(1);
    AtLabels.clickLabel("1");

    const restored = await LabelStudio.serialize();

    Asserts.notDeepEqualWithTolerance(result[0], restored[0], 1);
    Asserts.deepEqualWithTolerance(Helpers.omitBy(result[0], isLabels), Helpers.omitBy(restored[0], isLabels), 1);
    assert.strictEqual(restored[0].value.labels.length, 0);
    await clickLabelWithLengthExpection(2, 1, 1);
    switch (type) {
      case SINGLE_TYPE:
        await clickLabelWithLengthExpection(3, 1, 1);
        break;
      case MULTIPLE_TYPE:
        await clickLabelWithLengthExpection(3, 2, 2);
        break;
    }
    await clickLabelWithLengthExpection(1, 0, 1);
    await clickLabelWithLengthExpection(2, 1, 1);
    await clickLabelWithLengthExpection(2, 0, 1);
    await clickLabelWithLengthExpection(1, 0, 1);

    I.pressKey(["u"]);

    await clickLabelWithSelectedExpection(3, 1);
    switch (type) {
      case SINGLE_TYPE:
        await clickLabelWithSelectedExpection(2, 1);
        break;
      case MULTIPLE_TYPE:
        await clickLabelWithSelectedExpection(2, 2);
        break;
    }
    await clickLabelWithSelectedExpection(1, 1);
    await clickLabelWithSelectedExpection(2, 1);
    await clickLabelWithSelectedExpection(2, 0);
    await clickLabelWithSelectedExpection(1, 1);
    await clickLabelWithSelectedExpection(1, 0);
  });
});

Scenario("Consistency of empty labels", async ({ I, LabelStudio, AtOutliner, AtImageView, AtLabels, AtPanels }) => {
  const AtDetailsPanel = AtPanels.usePanel(AtPanels.PANEL.DETAILS);
  const { config, data } = require("../examples/image-bboxes");
  const params = { annotations: [{ id: "test", result: [] }], data };
  const configTree = Utils.parseXml(config);

  Utils.xmlForEachNode(configTree, (node) => {
    if (node["#name"].toLowerCase().endsWith("labels") && node.$) {
      node.$.allowempty = true;
    }
  });
  params.config = Utils.renderXml(configTree);

  I.amOnPage("/");
  LabelStudio.init(params);
  AtDetailsPanel.collapsePanel();
  AtOutliner.seeRegions(0);
  LabelStudio.waitForObjectsReady();
  AtLabels.clickLabel("1");
  AtImageView.dragKonva(200, 200, 100, 100);
  const shapesNum = await AtImageView.countKonvaShapes();

  assert.strictEqual(shapesNum, 1);
  const restored = await LabelStudio.serialize();
  const canvasSize = await AtImageView.getCanvasSize();
  const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height);

  Asserts.deepEqualWithTolerance(
    restored[0].value,
    convertToImageSize({ x: 200, y: 200, width: 100, height: 100, rotation: 0, rectanglelabels: [] }),
    1,
  );
});
